﻿using AMEC.PCSoftware.CommunicationProtocol.CrazyHein.FINS.IOUtility;
using AMEC.PCSoftware.CommunicationProtocol.CrazyHein.FINS.Message;
using System.Runtime.InteropServices;

namespace AMEC.PCSoftware.CommunicationProtocol.CrazyHein.FINS.Master
{
    public class MemoryAreaAccessMaster
    {
        private SocketInterface __socket;
        private byte __client_node_addr;
        private byte __server_node_addr;
        private byte[] __send_buffer;
        private byte[] __recv_buffer;
        private object __sync_object;

        public SocketInterface COM
        {
            set
            {
                lock (__sync_object)
                {
                    __socket = value;
                }
            }
        }
        public byte ClientNodeAddress
        {
            set
            {
                lock (__sync_object)
                {
                    __client_node_addr = value;
                }
            }
        }

        public byte ServerNodeAddress
        {
            set
            {
                lock (__sync_object)
                {
                    __server_node_addr = value;
                }
            }
        }

        public MemoryAreaAccessMaster(SocketInterface sc, byte client, byte server, byte[]? sendBuffer, byte[]? recvBuffer, object? sync = null)
        {
            __socket = sc;
            __client_node_addr = client;
            __server_node_addr = server;
            __send_buffer = sendBuffer ?? new byte[Marshal.SizeOf<MESSAGE_T>()];
            __recv_buffer = recvBuffer ?? new byte[Marshal.SizeOf<MESSAGE_T>()];
            __sync_object = sync ?? new object();
        }

        public ushort[]? ReadInWord(string code, ushort bytePos, ushort number, out ushort error)
        {
            lock (__sync_object)
            {
                int length = Command.MemoryAreaAccess.BUILD_READ_REQUEST(__server_node_addr, __client_node_addr, code, bytePos, 0, number, __send_buffer);
                __socket.Send(__send_buffer, 0, length);

                length = Message.Message.RECV(__socket, __recv_buffer, 0);
                var ret = Command.MemoryAreaAccess.PARSE_RESPONSE(new ReadOnlyMemory<byte>(__recv_buffer, 0, length), out byte client, out byte server, out Command.FINS_MAIN_COMMANDS_T main, out Command.FINS_SUB_COMMANDS_T sub, out error);

                if (main != Command.FINS_MAIN_COMMANDS_T.IO_MEMORY_AREA_ACCESS || sub != Command.FINS_SUB_COMMANDS_T.READ)
                    throw new FINSException(FINS_EXCEPTION_CODE_T.RECEIVED_UNMATCHED_COMMAND);
                if ((__client_node_addr != 0 && client != __client_node_addr) || server != __server_node_addr)
                    throw new FINSException(FINS_EXCEPTION_CODE_T.RECEIVED_UNMATCHED_HANDSHAKE_INFO);

                return ret;
            }
        }

        public void WriteInWord(string code, ushort bytePos, ushort number, ReadOnlyMemory<ushort> data, out ushort error)
        {
            lock (__sync_object)
            {
                int length = Command.MemoryAreaAccess.BUILD_WRITE_REQUEST(__server_node_addr, __client_node_addr, code, bytePos, 0, number, data, __send_buffer);
                __socket.Send(__send_buffer, 0, length);

                length = Message.Message.RECV(__socket, __recv_buffer, 0);
                Command.MemoryAreaAccess.PARSE_RESPONSE(new ReadOnlyMemory<byte>(__recv_buffer, 0, length), out byte client, out byte server, out Command.FINS_MAIN_COMMANDS_T main, out Command.FINS_SUB_COMMANDS_T sub, out error);

                if (main != Command.FINS_MAIN_COMMANDS_T.IO_MEMORY_AREA_ACCESS || sub != Command.FINS_SUB_COMMANDS_T.WRITE)
                    throw new FINSException(FINS_EXCEPTION_CODE_T.RECEIVED_UNMATCHED_COMMAND);
                if ((__client_node_addr != 0 && client != __client_node_addr) || server != __server_node_addr)
                    throw new FINSException(FINS_EXCEPTION_CODE_T.RECEIVED_UNMATCHED_HANDSHAKE_INFO);
            }
        }
    }
}
